﻿using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Linq;
using MathService.Annotations;

namespace System
{
    ///<summary>Методы расширения для массивов</summary>
    public static class ArrayExtantions
    {
        public static T[][] Split<T>(this T[] array, Func<T, bool> Splitter)
        {
            Contract.Requires(array != null);
            Contract.Requires(Splitter != null);
            Contract.Ensures(Contract.Result<T[][]>() != null);

            var result = new List<T[]>(array.Length);
            var aggregator = new List<T>(array.Length);

            for(var i = 0; i < array.Length; i++)
            {
                var value = array[i];
                if(Splitter(value) && aggregator.Count != 0)
                {
                    result.Add(aggregator.ToArray());
                    aggregator.Clear();
                }
                else
                    aggregator.Add(value);
            }
            if(aggregator.Count != 0)
                result.Add(aggregator.ToArray());

            return result.ToArray();
        }

        ///// <summary>
        ///// Быстрая сортировка Хоара
        ///// </summary>
        ///// <param name="A"></param>
        ///// <param name="low"></param>
        ///// <param name="high"></param>
        //public static void qSort(this int[] A, int low, int high)
        //{
        //    var i = low;
        //    var j = high;
        //    var x = A[(low + high) / 2];  // x - опорный элемент посредине между low и high
        //    do
        //    {
        //        while(A[i] < x) ++i;  // поиск элемента для переноса в старшую часть
        //        while(A[j] > x) --j;  // поиск элемента для переноса в младшую часть
        //        if(i > j) continue;
        //        // обмен элементов местами:
        //        var temp = A[i];
        //        A[i] = A[j];
        //        A[j] = temp;
        //        // переход к следующим элементам:
        //        i++;
        //        j--;
        //    } while(i < j);
        //    if(low < j) qSort(A, low, j);
        //    if(i < high) qSort(A, i, high);
        //}

        /// <summary>Быстрая сортировка Хоара</summary>
        /// <typeparam name="T">Тип сортируемых элементов</typeparam>
        /// <param name="A">Сортируемый массив элементов</param>
        /// <param name="low">Нижняя граница индекса сортировки</param>
        /// <param name="high">Верхняя граница индекса сортировки</param>
        public static void qSort<T>(this T[] A, int low, int high) where T : IComparable
        {
            Contract.Requires(A != null);
            Contract.Requires(low >= 0);
            Contract.Requires(high < A.Length);
            Contract.Requires(low < high);

            var i = low;
            var j = high;
            var x = A[(low + high) / 2];  // x - опорный элемент посредине между low и high
            do
            {
                while(A[i].CompareTo(x) < 0) ++i;  // поиск элемента для переноса в старшую часть
                while(A[j].CompareTo(x) > 0) --j;  // поиск элемента для переноса в младшую часть
                if(i > j) continue;
                // обмен элементов местами:
                var temp = A[i];
                A[i] = A[j];
                A[j] = temp;
                // переход к следующим элементам:
                i++;
                j--;
            } while(i < j);
            if(low < j) qSort(A, low, j);
            if(i < high) qSort(A, i, high);
        }

        /// <summary>Быстрая сортировка Хоара</summary>
        /// <typeparam name="T">Тип сортируемых элементов</typeparam>
        /// <param name="A">Сортируемый массив элементов</param>
        /// <param name="low">Нижняя граница индекса сортировки</param>
        /// <param name="high">Верхняя граница индекса сортировки</param>
        public static void qSort_t<T>(T[] A, int low, int high) where T : IComparable<T>
        {
            Contract.Requires(A != null);
            Contract.Requires(low >= 0);
            Contract.Requires(high < A.Length);
            Contract.Requires(low < high);

            var i = low;
            var j = high;
            var x = A[(low + high) / 2];  // x - опорный элемент посредине между low и high
            do
            {
                while(A[i].CompareTo(x) < 0) ++i;  // поиск элемента для переноса в старшую часть
                while(A[j].CompareTo(x) > 0) --j;  // поиск элемента для переноса в младшую часть
                if(i > j) continue;
                // обмен элементов местами:
                var temp = A[i];
                A[i] = A[j];
                A[j] = temp;
                // переход к следующим элементам:
                i++;
                j--;
            } while(i < j);
            if(low < j) qSort_t(A, low, j);
            if(i < high) qSort_t(A, i, high);
        }

        /// <summary>Рассчёт хеш-суммы всех элементов массива</summary>
        /// <typeparam name="T">Тип элементов</typeparam>
        /// <param name="Objects">Массив элементов</param>
        /// <returns>Хеш-сумма элементов массива</returns>
        public static int GetComplexHashCode<T>(this T[] Objects)
        {
            Contract.Requires(Objects != null);

            unchecked
            {
                var result = 397;
                for(var i = 0; i < Objects.Length; i++)
                    result = (result * 397) ^ Objects[i].GetHashCode();
                return result;
            }
        }

        ///<summary>Конвертация элементов массива</summary>
        ///<param name="In">Входной массив</param>
        ///<param name="converter">Метод конвертаци</param>
        ///<typeparam name="TIn">Тип элементов входного массива</typeparam>
        ///<typeparam name="TOut">Тип элементов выходного массива</typeparam>
        ///<returns>Массив элементов нового типа</returns>
        [Obsolete("Используйте LINQ.Select(...)", true)]
        [DebuggerStepThrough]
        public static TOut[] ConvertAll<TIn, TOut>(this TIn[] In, Converter<TIn, TOut> converter)
        {
            Contract.Requires(In != null);
            Contract.Requires(converter != null);
            Contract.Ensures(Contract.Result<TOut[]>() != null);

            return Array.ConvertAll(In, converter);
        }

        /// <summary>Получить копию массива</summary>
        /// <typeparam name="TArray">Тип исходного массива</typeparam>
        /// <param name="Array">Исходный массив</param>
        /// <returns>Новый массив</returns>
        [Obsolete("Используйте (T[])array.Clone()", true)]
        [DebuggerStepThrough]
        public static TArray[] GetCopy<TArray>(this TArray[] Array)
        {
            Contract.Requires(Array != null);
            Contract.Ensures(Contract.Result<TArray[]>() != null);
            Contract.Ensures(Contract.Result<TArray[]>().Length == Array.Length);

            return (TArray[])Array.Clone();
        }

        ///<summary>Объединение с массивом элементов</summary>
        ///<param name="A">Исходный массив</param>
        ///<param name="B">Присоединяемый массив</param>
        ///<typeparam name="TArray">Тип элементов массива</typeparam>
        ///<returns>Массив из объединёных элементов</returns>
        [DebuggerStepThrough]
        public static TArray[] Concatinate<TArray>(this TArray[] A, params TArray[] B)
        {
            Contract.Requires(A != null);
            Contract.Requires(B != null);
            Contract.Ensures(Contract.Result<TArray[]>() != null);
            Contract.Ensures(Contract.Result<TArray[]>().Length == A.Length + B.Length);

            var Result = new TArray[A.Length + B.Length];
            A.CopyTo(Result, 0);
            B.CopyTo(Result, A.Length);
            return Result;
        }

        public static TArray[] Concatinate<TArray>(this TArray[] A, params TArray[][] B)
        {
            Contract.Requires(A != null);
            Contract.Requires(B != null);
            Contract.Ensures(Contract.Result<TArray[]>() != null);
            Contract.Ensures(Contract.Result<TArray[]>().Length == A.Length + B.Length);

            var length = A.Length;
            for(var i = 0; i < B.Length; i++)
                length += B[i].Length;

            var Result = new TArray[length];
            A.CopyTo(Result, 0);
            var pos = A.Length;
            for(var i = 0; i < B.Length; i++)
            {
                var b = B[i];
                b.CopyTo(Result, pos += b.Length);
            }

            return Result;
        }

        //public delegate Complex BeamPattern(double Thetta, double Phi);

        ///<summary>Получить элемент массива</summary>
        ///<param name="A">Массив элементов</param>
        ///<param name="Selector">Метод выбора элемента массива</param>
        ///<typeparam name="TArray">Тип элементов массива</typeparam>
        ///<typeparam name="TOut">Тип выходного элемента</typeparam>
        ///<returns>Выбранный элемент массива</returns>
        [DebuggerStepThrough]
        public static TOut GetSelectedValue<TArray, TOut>(this TArray[] A, Func<TArray, TOut, TOut> Selector)
        {
            Contract.Requires(Selector != null);
            Contract.Requires(A != null);

            var lv_Out = default(TOut);
            var lv_ILength = A.Length;
            for(var i = 0; i < lv_ILength; i++)
                lv_Out = Selector(A[i], lv_Out);
            return lv_Out;
        }

        ///<summary>Преобразовать тип элементов массива</summary>
        ///<param name="In">Исходный массив элементов</param>
        ///<param name="converter">Метод преобразования элемента массива</param>
        ///<typeparam name="TIn">Исходный тип элементов массива</typeparam>
        ///<typeparam name="TOut">Требуемый тип элементов массива</typeparam>
        ///<returns>Массив преобразованных элементов</returns>
        [DebuggerStepThrough]
        public static TOut[] ConvertTo<TIn, TOut>(this TIn[] In, Converter<TIn, TOut> converter)
        {
            Contract.Requires(In != null);
            Contract.Requires(converter != null);
            Contract.Ensures(Contract.Result<TOut[]>() != null);

            var lv_Out = new TOut[In.Length];
            var i_Length = In.Length;
            for(var i = 0; i < i_Length; i++)
                lv_Out[i] = converter(In[i]);
            return lv_Out;
        }

        ///<summary>Выполнение действия для всех элементов массива</summary>
        ///<param name="array">Массив элементов</param>
        ///<param name="action">Выполняемой действие</param>
        ///<typeparam name="TArray">Тип элементов массива</typeparam>
        [DebuggerStepThrough]
        public static void Foreach<TArray>(this TArray[] array, Action<TArray> action)
        {
            Contract.Requires(array != null);
            Contract.Requires(action != null);
            Array.ForEach(array, action);
        }

        ///<summary>Выполнение действия для всех элементов массива с обработкой исключений</summary>
        ///<param name="array">Массив элементов</param>
        ///<param name="action">Выполняемое действие</param>
        ///<param name="ErrorHandler">Обработчик исключения</param>
        ///<typeparam name="TArray">Тип элементов массива</typeparam>
        [DebuggerStepThrough]
        public static void Foreach<TArray>(this TArray[] array, Action<TArray> action, Func<Exception, bool> ErrorHandler)
        {
            Contract.Requires(array != null);
            Contract.Requires(action != null);
            Contract.Requires(ErrorHandler != null);

            array.Foreach<TArray, Exception>(action, ErrorHandler);
        }

        ///<summary>Выполнение действия для всех элементов массива</summary>
        ///<param name="array">Массив элементов</param>
        ///<param name="action">ВЫполняемое действие</param>
        ///<param name="ErrorHandler">Обработчик исключений</param>
        ///<typeparam name="TArray">Тип элементов массива</typeparam>
        ///<typeparam name="TException">Тип исключений</typeparam>
        [DebuggerStepThrough]
        public static void Foreach<TArray, TException>(this TArray[] array, Action<TArray> action,
            Func<TException, bool> ErrorHandler) where TException : Exception
        {
            Contract.Requires(array != null);
            Contract.Requires(action != null);

            var length = array.Length;
            for(var i = 0; i < length; i++)
                try
                {
                    action(array[i]);
                } catch(TException e)
                {
                    if(ErrorHandler == null || !ErrorHandler(e))
                        throw new Exception("Ошибка выполнения", e);
                }
        }

        ///<summary>Определение значения функции для всех элементов массива</summary>
        ///<param name="array">Массив элементов</param>
        ///<param name="f">Вычисляемая функция</param>
        ///<typeparam name="TIn">Тип элементов массива области определения</typeparam>
        ///<typeparam name="TOut">Тип элементов массива области значения</typeparam>
        ///<returns>Массив значений функции</returns>
        [DebuggerStepThrough]
        public static TOut[] Function<TIn, TOut>(this TIn[] array, Func<TIn, TOut> f)
        {
            Contract.Requires(f != null);
            Contract.Requires(array != null);
            Contract.Ensures(Contract.Result<TOut[]>() != null);
            return array.Select(f).ToArray();
            //return array.ConvertAll(x => f(x));
        }

        [DebuggerStepThrough]
        public static TArray[] GetReversed<TArray>(this TArray[] array)
        {
            Contract.Requires(array != null);
            Contract.Ensures(Contract.Result<TArray[]>() != null);
            Contract.Ensures(Contract.Result<TArray[]>().Length == array.Length);

            var len = array.Length;
            var result = new TArray[len];
            for(var i = 0; i < len; i++)
                result[len - i - 1] = array[i];
            return result;
            //(array = (TArray[])array.Clone()).Reverse();
            //return array;
        }

        [DebuggerStepThrough]
        public static TArray[] GetSubArray<TArray>(this TArray[] array, int Length, int Start = 0)
        {
            Contract.Requires(array != null);
            Contract.Requires(Length >= 0);
            Contract.Ensures(Contract.Result<TArray[]>() != null);
            Contract.Ensures(Contract.Result<TArray[]>().Length == Length);

            var lv_Result = new TArray[Length];

            var j_Length = Start + Length;
            for(int i = 0, j = Start; i < Length && j < j_Length; i++, j++)
                lv_Result[i] = array[j];

            return lv_Result;
        }

        /// <summary>
        /// Инициализация массива
        /// </summary>
        /// <typeparam name="TArray">Тип элементов масива</typeparam>
        /// <param name="array">Инициализированный масив</param>
        /// <param name="Initializer">МЕтод инициализации</param>
        /// <returns>Инициализированный массив</returns>
        [DebuggerStepThrough]
        public static TArray[] Initialize<TArray>(this TArray[] array, Func<int, TArray> Initializer)
        {
            Contract.Requires(array != null);
            Contract.Requires(Initializer != null);
            Contract.Ensures(Contract.Result<TArray[]>() != null);
            Contract.Ensures(Contract.Result<TArray[]>() == array);

            for(var i = 0; i < array.Length; i++)
                array[i] = Initializer(i);
            return array;
        }

        [DebuggerStepThrough]
        public static TArray[] Initialize<TArray>(this TArray[] array, Func<TArray, int, TArray> Initializer)
        {
            Contract.Requires(array != null);
            Contract.Requires(Initializer != null);
            Contract.Ensures(Contract.Result<TArray[]>() != null);

            for(var i = 0; i < array.Length; i++)
                array[i] = Initializer(array[i], i);
            return array;
        }

        [DebuggerStepThrough]
        public static TArray[,] Initialize<TArray>(this TArray[,] array, Func<int, int, TArray> Initializer)
        {
            Contract.Requires(array != null);
            Contract.Requires(Initializer != null);
            Contract.Ensures(Contract.Result<TArray[,]>() != null);

            var length_i = array.GetLength(0);
            var length_j = array.GetLength(1);
            for(var i = 0; i < length_i; i++)
                for(var j = 0; j < length_j; j++)
                    array[i, j] = Initializer(i, j);
            return array;
        }

        [DebuggerStepThrough]
        public static TArray[][] Initialize<TArray>(this TArray[][] array, Func<int, TArray[]> ArrayInitializer, Func<int, int, TArray> Initializer)
        {
            Contract.Requires(array != null);
            Contract.Requires(ArrayInitializer != null);
            Contract.Requires(Initializer != null);
            Contract.Ensures(Contract.Result<TArray[][]>() != null);

            return array.Initialize(i => ArrayInitializer(i).Initialize(j => Initializer(i, j)));
        }

        [DebuggerStepThrough]
        public static void Reverse<TArray>(this TArray[] array)
        {
            Contract.Requires(array != null);

            Array.Reverse(array);
        }

        [DebuggerStepThrough]
        public static void SetValues<TArray>(this TArray[] array, int StartIndex, params TArray[] Values)
        {
            Contract.Requires(array != null);
            Contract.Requires(StartIndex >= 0);
            Contract.Requires(StartIndex < array.Length);
            Contract.Requires(Values != null);
            Contract.Requires(Values.Length > 0);

            for(var i = 0; ((i + StartIndex) < array.Length) && (i < Values.Length); i++)
                array[i + StartIndex] = Values[i];
        }

        public static TArray[,] ToAligned<TArray>(this TArray[][] array)
        {
            Contract.Requires(array != null);
            Contract.Ensures(Contract.Result<TArray[,]>() != null);

            var l = array.Select(a => a.Length).ToArray();
            return new TArray[array.Length, l.Max()].Initialize((i, j) => j >= l[i] ? default(TArray) : array[i][j]);
        }

        [DebuggerStepThrough]
        public static TArray[][] ToNonaligned<TArray>(this TArray[,] array)
        {
            Contract.Requires(array != null);
            Contract.Requires(array.GetLength(0) > 0);
            Contract.Requires(array.GetLength(1) > 0);
            Contract.Ensures(Contract.Result<TArray[][]>() != null);

            return new TArray[array.GetLength(0)][].Initialize(i => new TArray[array.GetLength(1)].Initialize(j => array[i, j]));
        }

        [DebuggerStepThrough]
        public static void SetCol<TArray>(this TArray[,] array, TArray[] Col, int m = 0)
        {
            Contract.Requires(array != null);
            Contract.Requires(Col != null);
            Contract.Requires(array.GetLength(0) != Col.Length, "Длина столбца не совпадает с высотой массива");
            Contract.Requires(m >= 0);
            Contract.Requires(m < array.GetLength(1), "Указанный номер столбца выходит за пределы размеров массива");

            var N = array.GetLength(0);
            var M = array.GetLength(1);

            if(N != Col.Length)
                throw new ArgumentException(string.Format("Длина столбца {0} не совпадает с высотой массива {1}",
                    Col.Length, N));
            if(m < 0 || m >= M)
                throw new ArgumentOutOfRangeException(nameof(m),
                    string.Format("Указанный номер столбца {0} выходит за пределы размеров массива {1}", m, M));
            for(var i = 0; i < N; i++)
                array[i, m] = Col[i];
        }

        [DebuggerStepThrough]
        public static void SetRow<TArray>(this TArray[,] array, TArray[] Row, int n = 0)
        {
            Contract.Requires(array != null);
            Contract.Requires(Row != null);
            Contract.Requires(array.GetLength(1) != Row.Length, "Длина строки не совпадает с шириной массива");
            Contract.Requires(n >= 0);
            Contract.Requires(n < array.GetLength(0), "Указанный номер строки выходит за пределы размеров массива");

            var N = array.GetLength(0);
            var M = array.GetLength(1);

            if(M != Row.Length)
                throw new ArgumentException(string.Format("Длина столбца {0} не совпадает с высотой массива {1}",
                    Row.Length, N));
            if(n < 0 || n >= N)
                throw new ArgumentOutOfRangeException(nameof(n),
                    string.Format("Указанный номер столбца {0} выходит за пределы размеров массива {1}", n, N));
            for(var i = 0; i < M; i++)
                array[n, i] = Row[i];
        }

        [DebuggerStepThrough]
        public static TArray[] GetCol<TArray>(this TArray[,] array, int m)
        {
            Contract.Requires(array != null);
            Contract.Requires(array.GetLength(0) > 0);
            Contract.Requires(array.GetLength(1) > 0);
            Contract.Requires(m >= 0);
            Contract.Requires(m < array.GetLength(1));
            Contract.Ensures(Contract.Result<TArray[]>() != null);

            var N = array.GetLength(0);
            var M = array.GetLength(1);

            if(m < 0 || m >= M)
                throw new ArgumentOutOfRangeException(nameof(m),
                    string.Format("Указанный номер столбца {0} выходит за пределы размеров массива {1}", m, M));

            var result = new TArray[N];
            for(var n = 0; n < N; n++)
                result[n] = array[n, m];
            return result;
        }

        [DebuggerStepThrough]
        public static TArray[] GetRow<TArray>(this TArray[,] array, int n)
        {
            Contract.Requires(array != null);
            Contract.Requires(n >= 0);
            Contract.Requires(n < array.GetLength(0));
            Contract.Ensures(Contract.Result<TArray[]>() != null);

            var N = array.GetLength(0);
            var M = array.GetLength(1);

            if(n < 0 || n >= N)
                throw new ArgumentOutOfRangeException(nameof(n),
                    string.Format("Указанный номер столбца {0} выходит за пределы размеров массива {1}", n, N));

            var result = new TArray[M];
            for(var m = 0; m < M; m++)
                result[m] = array[n, m];
            return result;
        }

        [DebuggerStepThrough]
        public static void SwapRows<T>(this T[,] array, int i, int j)
        {
            Contract.Requires(array != null);
            Contract.Requires(i >= 0);
            Contract.Requires(i < array.GetLength(1));
            Contract.Requires(j >= 0);
            Contract.Requires(j < array.GetLength(1));

            var N = array.GetUpperBound(0);
            for(var ii = 0; ii <= N; ii++)
            {
                var temp = array[i, ii];
                array[i, ii] = array[j, ii];
                array[j, ii] = temp;
            }
        }

        /// <summary>Поменять местами два столбца двумерного массива</summary>
        /// <param name="array">Двумерный массив</param>
        /// <param name="i">Номер первого столбца</param>
        /// <param name="j">Номер второго столбца</param>
        /// <typeparam name="T">Тип элементов массива</typeparam>
        [DebuggerStepThrough]
        public static void SwapCols<T>(this T[,] array, int i, int j)
        {
            Contract.Requires(array != null);
            Contract.Requires(i >= 0);
            Contract.Requires(i < array.GetLength(0));
            Contract.Requires(j >= 0);
            Contract.Requires(j < array.GetLength(0));

            var N = array.GetUpperBound(1);
            for(var ii = 0; ii <= N; ii++)
            {
                var temp = array[ii, i];
                array[ii, i] = array[ii, j];
                array[ii, j] = temp;
            }
        }

        public static int GetMinIndex<T>(this T[] array, Comparison<T> compare)
        {
            Contract.Requires(array != null);
            Contract.Requires(array.Length > 0);
            Contract.Requires(compare != null);
            Contract.Ensures(Contract.Result<int>() >= 0);
            Contract.Ensures(Contract.Result<int>() < array.Length);
            var i_min = 0;
            var min = array[i_min];
            for(var i = 1; i < array.Length; i++)
            {
                var v = array[i];
                if(compare(min, v) >= 0) continue;
                i_min = i;
                min = v;
            }
            return i_min;
        }

        public static int GetMinIndex<T>(this T[] array) where T : IComparable<T>
        {
            Contract.Requires(array != null);
            Contract.Requires(array.Length > 0);
            Contract.Ensures(Contract.Result<int>() >= 0);
            Contract.Ensures(Contract.Result<int>() < array.Length);
            var i_min = 0;
            var min = array[i_min];
            for(var i = 1; i < array.Length; i++)
            {
                var v = array[i];
                if(min == null || min.CompareTo(v) >= 0) continue;
                i_min = i;
                min = v;
            }
            return i_min;
        }

        public static int GetMaxIndex<T>(this T[] array, Comparison<T> compare)
        {
            Contract.Requires(array != null);
            Contract.Requires(array.Length > 0);
            Contract.Requires(compare != null);
            Contract.Ensures(Contract.Result<int>() >= 0);
            Contract.Ensures(Contract.Result<int>() < array.Length);
            var i_max = 0;
            var max = array[i_max];
            for(var i = 1; i < array.Length; i++)
            {
                var v = array[i];
                if(compare(max, v) <= 0) continue;
                i_max = i;
                max = v;
            }
            return i_max;
        }

        public static int GetMaxIndex<T>(this T[] array) where T : IComparable<T>
        {
            Contract.Requires(array != null);
            Contract.Requires(array.Length > 0);
            Contract.Ensures(Contract.Result<int>() >= 0);
            Contract.Ensures(Contract.Result<int>() < array.Length);
            var i_max = 0;
            var max = array[i_max];
            for(var i = 1; i < array.Length; i++)
            {
                var v = array[i];
                if(max == null || max.CompareTo(v) <= 0) continue;
                i_max = i;
                max = v;
            }
            return i_max;
        }

        /// <summary>Создать массив последовательных значений длины <paramref name="length"/> начиная с <paramref name="offset"/></summary>
        /// <param name="length">Длина массива</param>
        /// <param name="offset">НАчальное значение</param>
        /// <returns>Массив чисел длины <paramref name="length"/> начиная с <paramref name="offset"/></returns>
        public static int[] CreateSequence(int length, int offset = 0)
        {
            Contract.Requires(length > 0);
            Contract.Requires(offset >= 0);
            Contract.Requires(offset < length);
            Contract.Ensures(Contract.Result<int[]>() != null);
            Contract.Ensures(Contract.Result<int[]>().Length == length);

            var result = new int[length];
            for(var i = 0; i < length; i++)
                result[i] = i + offset;
            return result;
        }

        /// <summary>Создать копию массива с перемешанным содержимым</summary>
        /// <param name="array">Исходный массив</param>
        /// <typeparam name="T">Тип элементов массива</typeparam>
        /// <returns>Копия исходного массива с перемешанным созержимым</returns>
        public static T[] Mix<T>(this T[] array)
        {
            Contract.Requires(array != null);
            Contract.Ensures(Contract.Result<T[]>() != null);

            return ((T[])array.Clone()).MixRef();
        }

        /// <summary>Перемешать массив</summary>
        /// <typeparam name="T">Тип элементов массива</typeparam>
        /// <param name="array">Перемешиваемый массив</param>
        /// <returns>Исходный массив с перемешанным содержимым</returns>
        public static T[] MixRef<T>(this T[] array)
        {
            Contract.Requires(array != null);
            Contract.Requires(array.Length > 0);
            Contract.Ensures(Contract.Result<T[]>() != null);

            var length = array.Length - 1;
            var rnd = new Random();
            var temp = array[0];
            var index = 0;
            for(var i = 1; i <= length; i++)
                array[index] = array[index = rnd.Next(length)];
            array[index] = temp;

            return array;
        }

        /// <summary>Последовательно скопировать набор массивов в буффер</summary>
        /// <param name="A">Буфферный массив соответствующей длины</param>
        /// <param name="B">Перечень устанавливаемых значений</param>
        /// <typeparam name="T">Тип элементов массива</typeparam>
        public static void SetSubArrays<T>(this T[] A, params T[][] B)
        {
            Contract.Requires(A != null);
            Contract.Requires(B != null);
            Contract.Requires(A.Length == B.Select(b => b.Length).Sum());

            var index = 0;
            B.Foreach(b =>
            {
                b.CopyTo(A, index);
                index += b.Length;
            });
        }

        public static bool IsContains<T>(this T[] array, T item)
        {
            Contract.Requires(array != null);
            return Array.IndexOf(array, item) != -1;
        }

        public static T[] Liniarize<T>(this T[][] array)
        {
            Contract.Requires(array != null);
            Contract.Ensures(Contract.Result<T[]>() != null);

            var result_length = 0;
            for(var i = 0; i < array.Length; i++)
                result_length += array[i].Length;
            if(result_length == 0) return new T[0];
            var result = new T[result_length];
            for(int i = 0, k = 0; i < array.Length; i++)
            {
                var sub_array = array[i];
                for(var j = 0; j < sub_array.Length; j++)
                    result[k++] = sub_array[j];
            }
            return result;
        }

        public static void Foreach<T>(this T[,] array, Action<T> action)
        {
            Contract.Requires(array != null);
            Contract.Requires(action != null);

            var I = array.GetLength(0);
            var J = array.GetLength(1);
            for(var i = 0; i < I; i++)
                for(var j = 0; j < J; j++)
                    action(array[i, j]);
        }

        public static void Foreach<T>(this T[,] array, Action<int, int, T> action)
        {
            Contract.Requires(array != null);
            Contract.Requires(action != null);

            var I = array.GetLength(0);
            var J = array.GetLength(1);
            for(var i = 0; i < I; i++)
                for(var j = 0; j < J; j++)
                    action(i, j, array[i, j]);
        }

        public static IEnumerable<Q> Select<T, Q>(this T[,] array, Func<T, Q> selector)
        {
            Contract.Requires(array != null);
            Contract.Requires(selector != null);
            Contract.Ensures(Contract.Result<IEnumerable<Q>>() != null);

            var I = array.GetLength(0);
            var J = array.GetLength(1);
            for(var i = 0; i < I; i++)
                for(var j = 0; j < J; j++)
                    yield return selector(array[i, j]);
        }

        public static IEnumerable<Q> Select<T, Q>(this T[,] array, Func<int, int, T, Q> selector)
        {
            var I = array.GetLength(0);
            var J = array.GetLength(1);
            for(var i = 0; i < I; i++)
                for(var j = 0; j < J; j++)
                    yield return selector(i, j, array[i, j]);
        }
    }


}
